# uncompyle6 version 3.2.3
# Python bytecode 3.6 (3379)
# Decompiled from: Python 3.6.8 |Anaconda custom (64-bit)| (default, Feb 21 2019, 18:30:04) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: helpers\account.py
__author__ = "Attila Gerendi (Sanyi)"
import time, datetime
from dateutil.tz import tzutc
from sqlalchemy import select as sql_select
from server.http_responses.http_errors import Http409, ApiErrors
from db.tables.pwd_history import PwdHistoryTable, PwdHistoryRow
from db.tables.users import UsersTable, UserRow
from api.classes.frontend import ClsAccountLockout, ClsAccountSecurity
from db.tables.ui_sessions import UiSessionRow, UiSessionsTable


def reset_ui_sessions(db, user_id):
    q = UiSessionsTable.delete().where(UiSessionRow.user_id == user_id)
    db.execute(q)


class AccountLockout:
    _AccountLockout__lock_data = dict()

    @staticmethod
    def is_account_locked(email):
        entry = AccountLockout._AccountLockout__lock_data.get(email)
        if not entry:
            return False
        elif entry["locked"] > time.time():
            return entry["locked"]
        else:
            return False

    @staticmethod
    def report_bad_login(
        email, lockout_time, lockout_window, lockout_count, just_recalculate=False
    ):
        t = time.time()
        entry = AccountLockout._AccountLockout__lock_data.get(email)
        if not entry:
            entry = dict(locked=0, history=[])
        else:
            for i in range(0, len(entry["history"])):
                if entry["history"][i] > t - lockout_window:
                    if i > 0:
                        entry["history"] = entry["history"][i:]
                    break
            else:
                entry["history"] = []

        if not just_recalculate:
            entry["history"].append(t)
        if len(entry["history"]) >= lockout_count:
            entry["locked"] = t + lockout_time
        AccountLockout._AccountLockout__lock_data[email] = entry
        return entry["locked"] > t

    @staticmethod
    def reset(email):
        try:
            del AccountLockout._AccountLockout__lock_data[email]
        except KeyError:
            pass


def require_password_is_not_reused(
    db, user_id, pwd_history_depth, new_password, current_password=None
):
    if not pwd_history_depth:
        return
    q = sql_select((PwdHistoryRow.pwd,)).where(PwdHistoryRow.user_id == user_id)
    pwd_list = db.execute(q).scalar()
    if pwd_list:
        found = True
        if new_password in pwd_list:
            raise Http409("password reused", code=ApiErrors.PASSWORD_REUSE_ERROR)
        else:
            found = False
            pwd_list = []
        if current_password is not None:
            pwd_list.append(current_password)
            pwd_list = pwd_list[:-pwd_history_depth]
            if found:
                (PwdHistoryTable.update(values=dict(pwd=pwd_list))).where(
                    PwdHistoryRow.user_id == user_id
                )
            else:
                PwdHistoryTable.insert().values(user_id=user_id, pwd=pwd_list)


def update_account_security(db, user, data):
    user_update = dict()
    max_session_time = data.get("max_session_time")
    if max_session_time is not None:
        if max_session_time < 0:
            max_session_time = 0
        if user["max_session"] != max_session_time:
            user_update["max_session"] = 0
    totp_required = data.get("totp_required")
    if totp_required is not None:
        if user["otp_required"] != totp_required:
            user_update["otp_required"] = totp_required
    password_history = data.get("password_history")
    if password_history is not None:
        if user["pwd_history"] != password_history:
            user_update["pwd_history"] = password_history
    password_max_age = data.get("password_max_age")
    if password_max_age is not None:
        if user["pwd_max_age"] != password_max_age:
            user_update["pwd_max_age"] = password_max_age
            if password_max_age:
                user_update["pwd_expires"] = datetime.datetime.now(tzutc()) + (
                    datetime.timedelta(days=password_max_age)
                )
            else:
                user_update["pwd_expires"] = None
            account_lockout = data.get("account_lockout")
            if account_lockout is not None:
                count = account_lockout.get("max_consecutive_login_failures", 0)
                new_lockout = dict(
                    time=account_lockout.get("lockout_time", 30),
                    count=count,
                    window=account_lockout.get("time_window", 60),
                )
                user_update["lockout"] = new_lockout
                if count == 0:
                    AccountLockout.reset(user["email"])
            if user_update:
                q = (UsersTable.update(values=user_update)).where(
                    UserRow.user_id == user["user_id"]
                )
                db.execute(q)


def get_account_security_settings(user):
    lockout = user.get("lockout")
    if not lockout or not lockout.get("count"):
        lockout = ClsAccountLockout(
            lockout_time=30, max_consecutive_login_failures=0, time_window=60
        )
    else:
        lockout = ClsAccountLockout(
            lockout_time=lockout.get("time", 30),
            max_consecutive_login_failures=lockout.get("count", 0),
            time_window=lockout.get("window", 60),
        )
    return ClsAccountSecurity(
        max_session_time=user["max_session"] if user["max_session"] else 0,
        totp_enabled=user["otp"] is not None,
        totp_required=user["otp_required"] if user["otp_required"] else False,
        password_history=user["pwd_history"] if user["pwd_history"] else 0,
        password_max_age=user["pwd_max_age"] if user["pwd_max_age"] else 0,
        account_lockout=lockout,
    )
